package com.techgene.kanban.controller;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;

import com.techgene.kanban.domain.Project;
import com.techgene.kanban.domain.RequestParamProject;
import com.techgene.kanban.domain.User;
import com.techgene.kanban.exception.KBServiceException;
import com.techgene.kanban.service.ProjectService;
import com.techgene.kanban.util.AppConstants;




/**
 * project handler adding projects with users
 * 
 * @author techgene
 * 
 */
@Controller
public class ProjectController {

	private Logger logger = LoggerFactory.getLogger(ProjectController.class);
	@Autowired
	ProjectService projectService;
	static List totalSelectedUsers = new ArrayList();	//list containing users that are selected from usersList and must be added to project(insertlist)
	static List usersList = new ArrayList();	//list containing users that match with searchtext
	static List projectSpecificUsers = new ArrayList();	//list containing users those already assigned to project and users to be updated(updatelist)
	static List users = new ArrayList();

	
	
	/**
	 * show projectcreateupdatedelete.jsp handler
	 * @param model Model
	 * @return 
	 */
	@RequestMapping(value = "/admin/add", method = RequestMethod.GET)
	public ModelAndView showProjectPage(Model model) {
		
		logger.debug(" >> showProjectPage");
		totalSelectedUsers.clear();
		usersList.clear();
		model.addAttribute("command", new RequestParamProject());
		logger.debug(" << showProjectPage [type = {} ] ", AppConstants.PROJECT_INSERT);
		return new ModelAndView(AppConstants.SHOW_PROJECT, "type", AppConstants.PROJECT_INSERT);
		
	}

	
	
	
	
	/**
	 * fetches all usernames that match with the searchText
	 * 
	 * @param searchText String
	 * @param type String
	 * @param flashData RedirectAttributes
	 * @param map Model
	 * @return
	 */
	@RequestMapping(value = "/admin/submitSearch", method = RequestMethod.GET)
	public ModelAndView submitSearch(@RequestParam String searchText,@RequestParam String type, RedirectAttributes flashData, Model map) {

		logger.debug(">> submitSearch , [ searchText = {} ,type = {} ] ",searchText, type);

		try {
			
			/*
			 * checking the type and based on type sending different list
			 */

			if (type.equalsIgnoreCase("update")) {
				
				synchronized (this) {
					/*calling retrieveUsers to retrieve users that match with searchText */
					usersList = projectService.retrieveUsers(searchText,projectSpecificUsers);
					logger.debug("usersList = {} ",usersList);
				}
				
			} else {
				
					synchronized(this){
					/*calling retrieveUsers to retrieve users that match with searchText */
					usersList = projectService.retrieveUsers(searchText,totalSelectedUsers);
					logger.debug("usersList = {} ",usersList);

				
				}
				
			}
			logger.debug(" usersList = {} ", usersList);
		
		} catch (KBServiceException e) {

			logger.error(e.getMessage(), e);
			flashData.addFlashAttribute("errorMsg", e.getMessage());
			return new ModelAndView(new RedirectView(AppConstants.COMMON_ERROR_PAGE));

		} catch (Exception e) {

			logger.error(e.getMessage(), e);
			flashData.addFlashAttribute("errorMsg", e.getMessage());
			return new ModelAndView(new RedirectView(AppConstants.COMMON_ERROR_PAGE));
		}
		
		map.addAttribute("type", type);
		logger.debug(" << submitSearch , [ usersList = {} ] ", usersList);
		return new ModelAndView(AppConstants.SHOW_USERS, "usersList", usersList);
	}
	
	
	
	

	/**
	 * deleting selected person from the usersList and adding to projectSpecificUsers or totalSelectedUsers based on type
	 * @param type String
	 * @param index
	 * @param map Model
	 * @return
	 */
	@RequestMapping(value = "/admin/selectUser", method = RequestMethod.POST)
	public ModelAndView selectUser(@RequestParam Integer index,@RequestParam String type, Model map) {

		logger.debug(" >> selectUser [ index = {} , type = {} ]", index,type);
		index = index - 1;
		logger.debug("usersList={},index={}", usersList.size(), index);
		User user = (User) usersList.get((Integer) index);
		logger.debug("before adding projectSpecificUserssize={},totalSelectedUserssize={}",projectSpecificUsers.size(), totalSelectedUsers.size());
		
		/* checking for type to add user from usersList to projectSpecificUsers or totalSelectedUsers */
		if (type.equalsIgnoreCase("update")) {
			
			synchronized(this){

				projectSpecificUsers.add(user);
				
			}
		} else {
			
			synchronized(this){

				totalSelectedUsers.add(user);
			}
		}
		
		logger.debug("after adding projectSpecificUserssize={},totalSelectedUserssize={}",projectSpecificUsers.size(), totalSelectedUsers.size());
		
		synchronized(this){

		for (Iterator<User> it = usersList.iterator(); it.hasNext();) {
			List t=Collections.synchronizedList(totalSelectedUsers);
			User usr = it.next();
			if (usr.equals(user)) {
				it.remove();
			}
		}
		
		}
		
		logger.debug("usersList={}", usersList.size());
		logger.debug("user={}", user);
		logger.info(user.getUsername());
		map.addAttribute("type", type);
		logger.debug(" << selectUser [ usersList={} ]", usersList);
		
		return new ModelAndView(AppConstants.SHOW_USERS, "usersList", usersList);

	}

	
	
	
	/**
	 * sending selected users to jsp
	 * @param type String
	 * @param index
	 * @param map Model
	 * @return
	 */
	@RequestMapping(value = "/admin/selectedUser", method = RequestMethod.GET)
	public ModelAndView selectedUser(@RequestParam String type, Model map) {

		logger.debug(" >> selectedUser [ type = {} ]",type);
		map.addAttribute("type", type);
		
		//checking for type based on type sending insertlist(totalSelectedUsers) or update list(projectSpecificUsers)
		if (type.equalsIgnoreCase("update")) {

			logger.debug(" << selectedUser [ projectSpecificUsers={} ]", projectSpecificUsers);
			return new ModelAndView(AppConstants.SELECTED_USERS,"selectedUsersList", projectSpecificUsers);
			
		} else {

			logger.debug(" << selectedUser [ totalSelectedUsers={} ]", totalSelectedUsers);
			return new ModelAndView(AppConstants.SELECTED_USERS,"selectedUsersList", totalSelectedUsers);
			
		}

	}

	
	
	
	/**
	 * deleting user from updatelist or insertlist
	 * @param type String
	 * @param index
	 * @param map Model
	 * @return
	 */
	@RequestMapping(value = "/admin/deleteUser", method = RequestMethod.POST)
	public ModelAndView deleteUser(@RequestParam Integer index,@RequestParam String type, Model map) {

		logger.debug(" >> deleteUser [ index = {} ,type={}]", index, type);
		index = index - 1;
		User user;
		map.addAttribute("type", type);
		logger.debug("projectspecificusers = {} ", projectSpecificUsers);
		
		//checking for type and deleting user from insertlist or updatelist
		if (type.equalsIgnoreCase("update")) {
			
			logger.info(type);
			synchronized(this){

				user = (User) projectSpecificUsers.get(index);
				for (Iterator<User> it = projectSpecificUsers.iterator(); it.hasNext();) {
					User usr = it.next();
					if (usr.equals(user)) {
						it.remove();
					}
				}
			}
			logger.debug(" << selectedUser [ projectSpecficUsers={} ]", projectSpecificUsers);
			return new ModelAndView(AppConstants.SELECTED_USERS,"selectedUsersList", projectSpecificUsers);

		} else {
			
			logger.info(type);
			synchronized(this){

				user = (User) totalSelectedUsers.get(index);
				for (Iterator<User> it = totalSelectedUsers.iterator(); it.hasNext();) {
					User usr = it.next();
					if (usr.equals(user)) {
						it.remove();
					}
				}
			}
			logger.debug(" << selectedUser [ totalSelectedUsers={} ]", totalSelectedUsers);
			return new ModelAndView(AppConstants.SELECTED_USERS,"selectedUsersList", totalSelectedUsers);
		}

	}

	
	
	
	
	/**
	 * adding project handler
	 * @param flashData RedirectAttributes
	 * @param type String
	 * @param model Model
	 * @param request  HttpServletRequest
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
	@RequestMapping(value = "/admin/addOrUpdateProject", method = RequestMethod.POST)
	public ModelAndView submitProject(HttpServletRequest request,RedirectAttributes flashData, Model model, @RequestParam String type) {

		logger.debug(" >> addProject [type = {} ] ", type);
		String projectName = request.getParameter("projectName");
		int projectId;
		
		if(request.getParameter("projectId")!=null){
			
			projectId =Integer.parseInt(request.getParameter("projectId"));
		}
		else{
			
			projectId=0;
		}
		
		logger.debug("projectid={}",projectId);
		String projectDescription = request.getParameter("projectDescription");
		logger.info(projectName);
		List users = new ArrayList();
		model.addAttribute("command", new RequestParamProject());
		int result;
		if (type.equalsIgnoreCase("update")) {
			
			logger.info(type);
			users = new ArrayList(projectSpecificUsers);
			
		} else {
			
			users = new ArrayList(totalSelectedUsers);
			
		}
		
		logger.debug("users = {} ,projectdes = {} ", users, projectDescription);
		
		try {
			/* calling addproject */
			result = projectService.addOrUpdateProject(projectName,projectDescription, users, type,projectId);
			logger.debug(" result={} ",result);

		} catch (KBServiceException e) {

			logger.debug("error= {}", e.getMessage());
			logger.error(e.getMessage(), e);
			flashData.addFlashAttribute("errorMsg", e.getMessage());
			return new ModelAndView(AppConstants.ADMIN_PAGE,"statusMsg", e.getMessage());

		} catch (Exception e) {

			logger.debug("error= {}", e.getMessage());
			logger.error(e.getMessage(), e);
			flashData.addFlashAttribute("errorMsg", e.getMessage());
			return new ModelAndView(AppConstants.ADMIN_PAGE,"statusMsg", e.getMessage());
		}

		logger.debug(" << submitProject ");
		return new ModelAndView(AppConstants.ADMIN_PAGE, "statusMsg",AppConstants.PROJECT_STATUSMSG);

	}

	
	
	
	/**
	 * listing project handler
	 * @param flashData RedirectAttributes
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	@RequestMapping(value = "/admin/listProjects", method = RequestMethod.GET)
	public ModelAndView showListProjectPage(RedirectAttributes flashData) {

		logger.debug(" >> showListProjectPage ");
		ModelAndView returnView = null;

		try {

			/* fetchig projects */
			List projectList = projectService.listProjects();
			logger.debug(" projects [projectList={}]", projectList);
			returnView = new ModelAndView(AppConstants.SHOW_PROJECT_LIST,"projects", projectList);

		} catch (KBServiceException e) {

			logger.error(e.getMessage(), e);
			flashData.addFlashAttribute("errorMsg", e.getMessage());
			returnView = new ModelAndView(new RedirectView(AppConstants.COMMON_ERROR_PAGE));

		} catch (Exception e) {

			logger.error(e.getMessage(), e);
			flashData.addFlashAttribute("errorMsg", e.getMessage());
			returnView = new ModelAndView(new RedirectView(AppConstants.COMMON_ERROR_PAGE));
		}

		logger.debug(" << showListProjectPage returnView = {} ", returnView);
		return returnView;
	}

	
	
	
	/**
	 * deleting project handler
	 * @param projectName String
	 * @param flashData RedirectAttributes
	 * @return
	 */
	@RequestMapping(value = "/admin/deleteprojectByName", method = RequestMethod.GET)
	public RedirectView submitDeleteProject(@RequestParam String projectName,RedirectAttributes flashData) {

		logger.debug(" >> deleteProject [ projectName = {} ]", projectName);
		RedirectView returnView = null;
		
		try {

			/*
			 * calling deleteProject in deleteProjectService implemented class
			 */
			int i = projectService.deleteProject(projectName);
			logger.debug(" result = {} ", i);
			/*
			 * checking whether deletion is success or not if success displays
			 * success msg else failure msg
			 */
			if (i == 1) {

				flashData.addFlashAttribute("statusMsg",AppConstants.DELETE_PROJECT_SUCCESS);
				returnView = new RedirectView(AppConstants.LOGIN_PAGE);

			} else {

				flashData.addFlashAttribute("statusMsg",AppConstants.DELETE_PROJECT_FAILURE);
				returnView = new RedirectView(AppConstants.LOGIN_PAGE);
				
			}
		} catch (KBServiceException e) {

			logger.error(e.getMessage(), e);
			flashData.addFlashAttribute("errorMsg", e.getMessage());
			returnView = new RedirectView(AppConstants.COMMON_ERROR_PAGE);

		} catch (Exception e) {

			logger.error(e.getMessage(), e);
			flashData.addFlashAttribute("errorMsg", e.getMessage());
			returnView = new RedirectView(AppConstants.COMMON_ERROR_PAGE);
		}

		logger.debug(" << submitDeleteProject [returnView = {} ]",returnView);
		return returnView;

	}

	
	
	
	/**
	 * displaying selected users
	 * @param model Model
	 * @return
	 */
	@RequestMapping(value = "/admin/showSelectedUsers", method = RequestMethod.GET)
	public ModelAndView showSelectedUsers(Model model) {

		logger.debug(" >> showSelectedUsers ");
		model.addAttribute("type", "update");
		logger.debug(" << showSelectedUsers [ projectSpecficUsers = {} ]",projectSpecificUsers);
		return new ModelAndView(AppConstants.SELECTED_USERS,"selectedUsersList", projectSpecificUsers);
	}

	
	
	
	/**
	 * update projecthandler
	 * @param model Model
	 * @param flashData RedirectAttributess
	 * @param projectName String
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	@RequestMapping(value = "/admin/updateProjectDetails", method = RequestMethod.GET)
	public ModelAndView showUpdateProjectPage(@RequestParam String projectName,Model model, RedirectAttributes flashData) {

		logger.debug(" >> showUpdateProjectPage [ projectName = {} ]",projectName);
		model.addAttribute("command", new RequestParamProject());
		model.addAttribute("projectName", projectName);
		
		try {

			/* retrieving users based on projectname */
			Map project = project = projectService.retrieveUsersOfProject(projectName);
			logger.debug("project={}",project);
			for (int i = 0; i < project.size(); i++) {
				
				model.addAttribute("projectName", project.get("projectName"));
				model.addAttribute("projectId", project.get("projectId"));
				model.addAttribute("projectDescription",project.get("projectDescription"));
				projectSpecificUsers = (ArrayList) project.get("users");
				
			}
			model.addAttribute("project", project);
			logger.debug(" project = {} ", project);

		} catch (KBServiceException e) {

			logger.error(e.getMessage(), e);
			flashData.addFlashAttribute("errorMsg", e.getMessage());
			return new ModelAndView(new RedirectView(AppConstants.COMMON_ERROR_PAGE));

		} catch (Exception e) {

			logger.error(e.getMessage(), e);
			flashData.addFlashAttribute("errorMsg", e.getMessage());
			return new ModelAndView(new RedirectView(AppConstants.COMMON_ERROR_PAGE));
		}
		
		logger.debug(" << showUpdateProjectPage [ type = {} ]",AppConstants.PROJECT_UPDATE);
		return new ModelAndView(AppConstants.SHOW_PROJECT, "type", AppConstants.PROJECT_UPDATE);
		
	}
}
